/*
 * Game.cpp
 *
 * Created on: Jun 15, 2014
 * 
 * Copyright 2013 Bruno Merlo Schurmann <brunomerloschurmann@gmail.com>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses 
 */

#include "Game.h"
#include <utility>
#include "MapStateObserver.h"
#include "ObjectStateObserver.h"
#include "ExplosionObserver.h"
#include "MapObject.h"
#include "Spawner.h"
#include "Plant.h"
#include "TimedBomb.h"
#include <iostream>
#include "ScoresObserver.h"
#include "Message.h"
#include "Utils.h"

using std::vector;
using std::pair;

Game::Game(Partida& game_room, Nivel& level)
    : game_room(game_room), level(level){
    map = new Map(level.getMapWidth(), level.getMapWidth());
    game_controller = new GameController(*map);
    load_representations();
}

void Game::loadLevel() {
    //Set-up bomberman spawns on map
    vector<pair<int, int> >& bomberman_spawns = level.getBombermanSpawnPoints();
    vector<pair<int, int> >::iterator bsp_it;
    for (bsp_it = bomberman_spawns.begin();
            bsp_it != bomberman_spawns.end();
            ++bsp_it) {
        map->addBombermanSpawn((*bsp_it).first, (*bsp_it).second);
    }

    //Set-up observers
    setUpObservers();

    //Assign a bomberman to each player and inserts them in the controller
    vector<JugadorConectado*>& players = game_room.getPlayers();
    std::size_t i;
    for (i = 0; i < players.size(); i++) {
        int player_id = players[i]->getId();
        pair<int, int> spawn_point = bomberman_spawns[i];
        Bomberman* bomberman = new Bomberman(player_id, spawn_point.first, spawn_point.second);
        bomberman->setRepresentation(bomberman_representations[i]);
        bombermans[player_id] = bomberman;
        game_controller->addBomberman(bomberman);
        Message message;
        message.code = NEW_PLAYER_ID;
        message.args.push_back(Utils::intToString(bomberman->getId()));
        game_room.notifyPlayer(player_id, message);
        std::cout << "Bomberman con id " << bomberman->getId() << " asignado al jugador con id " << player_id << std::endl;
    }

    //Set-up objects
    game_controller->setRemainingEnemies(level.getNumberOfEnemies());
    //Boxes and walls
    vector<MapObject*>& solids = level.getMapObjects();
    vector<MapObject*>::iterator solids_it;
    for (solids_it = solids.begin();
            solids_it != solids.end();
            ++solids_it) {
        game_controller->addSolid((*solids_it)->clone());
    }
    //Enemy spawners
    vector<Spawner*>& spawners = level.getSpawners();
    vector<Spawner*>::iterator spawners_it;
    for (spawners_it = spawners.begin();
            spawners_it != spawners.end();
            ++spawners_it) {
        game_controller->addSpawner((*spawners_it)->clone());
    }
    //Plants
    vector<Plant*>& plants = level.getPlants();
    vector<Plant*>::iterator plants_it;
    for (plants_it = plants.begin(); plants_it != plants.end(); ++plants_it) {
        Plant* plant = (*plants_it)->clone();
        game_controller->addLivingObject(plant);
        game_controller->addSpawner(plant, true);
    }
}

GameState Game::getState() {
    return game_controller->getState();
}

bool Game::moveUp(int player_id) {
    return moveBomberman(player_id, 0, -1);
}

bool Game::moveRight(int player_id) {
    return moveBomberman(player_id, 1, 0);
}

bool Game::moveDown(int player_id) {
    return moveBomberman(player_id, 0, 1);
}

bool Game::moveLeft(int player_id) {
    return moveBomberman(player_id, -1, 0);
}

bool Game::plantBomb(int player_id) {
    if (! isRunning()) {
        return false;
    }
    Bomberman* bomberman = bombermans[player_id];
    if (! bomberman->hasEnteredTheMap()) {
        return false;
    }
    if (map->objectDropedOn(bomberman->getX(), bomberman->getY())) {
        return false;
    }
    TimedBomb* bomb = bomberman->dropBomb();
    if (bomb == 0) {
        return false;
    }
    game_controller->addLivingObject(bomb, DROP);
    return true;
}

bool Game::isRunning() {
    return game_controller->isRunning();
}

void Game::startGame() {
    game_controller->startGame();
}

void Game::stopGame() {
    game_controller->stopGame();
}

Game::~Game() {
    delete game_controller;
    delete map;
    delete map_observer;
}

void Game::setUpObservers() {
    //Map observers
    map_observer = new MapStateObserver(game_room);
    map->attach(map_observer);

    //Objects observers
    ObjectStateObserver* obs_state = new ObjectStateObserver(game_room);
    game_controller->addObserver(obs_state);
    ExplosionObserver* obs_explosion = new ExplosionObserver(game_room, *map);
    game_controller->addObserver(obs_explosion);
    ScoresObserver* obs_scores = new ScoresObserver(game_room);
    game_controller->addObserver(obs_scores);
}

bool Game::moveBomberman(int player_id, int dx, int dy) {
    if (! isRunning()) {
        return false;
    }
    Bomberman* bomberman = bombermans[player_id];
    if (! bomberman->hasEnteredTheMap()) {
        return false;
    }
    int x = bomberman->getX() + dx;
    int y = bomberman->getY() + dy;
    if (! map->isValidPosition(x, y)) {
        return false;
    }
    bomberman->movingTo(x, y);
    return true;
}

void Game::load_representations() {
    bomberman_representations.push_back(BOMBERMAN_WHITE);
    bomberman_representations.push_back(BOMBERMAN_BLACK);
    bomberman_representations.push_back(BOMBERMAN_RED);
    bomberman_representations.push_back(BOMBERMAN_PURPLE);
    bomberman_representations.push_back(BOMBERMAN_GREEN);
    bomberman_representations.push_back(BOMBERMAN_YELLOW);
}
